/*********************************************************************
*                SEGGER Microcontroller GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
**********************************************************************
*                                                                    *
*        (c) 1996 - 2017  SEGGER Microcontroller GmbH & Co. KG       *
*                                                                    *
*        Internet: www.segger.com    Support:  support@segger.com    *
*                                                                    *
**********************************************************************

** emWin V5.46 - Graphical user interface for embedded applications **
emWin is protected by international copyright laws.   Knowledge of the
source code may not be used to write a similar product.  This file may
only  be used  in accordance  with  a license  and should  not be  re-
distributed in any way. We appreciate your understanding and fairness.
----------------------------------------------------------------------
File        : Bounce.c
Purpose     : Bouncing balls demo
---------------------------END-OF-HEADER------------------------------
*/

#include <stddef.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>

#include "DIALOG.h"

/*********************************************************************
*
*       Define
*
**********************************************************************
*/
#define ID_WINDOW_0   (GUI_ID_USER + 0x00)
#define ID_BUTTON_0   (GUI_ID_USER + 0x01)
#define ID_BUTTON_1   (GUI_ID_USER + 0x02)
#define ID_BUTTON_2   (GUI_ID_USER + 0x03)
#define ID_CHECKBOX_0 (GUI_ID_USER + 0x04)
#define ID_CHECKBOX_1 (GUI_ID_USER + 0x05)
#define ID_TEXT_0     (GUI_ID_USER + 0x06)
#define ID_SLIDER_0   (GUI_ID_USER + 0x07)

#define MIN_RANDOM_V  0           // Minimum velocity (x or y) to be used in generating random balls
#define MAX_RANDOM_V  80          // Maximum velocity (x or y) to be used in generating random balls
#define MIN_RANDOM_R  5           // Minimum radius to be used in generating random balls
#define MAX_RANDOM_R  40          // Maximum radius to be used in generating random balls
#define M_PI          3.1415926f
#define M_TO_A_RATIO  0.1f        // Ratio of mass to area used in generating random balls

#define TIME_SLICE    20
#define NUM_BALLS     10
#define GRAVITY       100

#define XSIZE_SPLASH  220
#define YSIZE_SPLASH  120
#define PERIOD_SPLASH 3000

#if GUI_VERSION < 54400
  #define WM_USER_DATA (WM_USER + 0)
#endif

/*********************************************************************
*
*       Types
*
**********************************************************************
*/
enum Wall { WALL_NONE, WALL_X1, WALL_Y1, WALL_X2, WALL_Y2 };
enum Type { TYPE_NONE, TYPE_WALL, TYPE_BALL };

typedef struct {
  float x, y;
} VECTOR;

typedef struct {
  float x1, y1, x2, y2;
} WALLS;

typedef struct BALL BALL;

struct BALL {
  VECTOR p;      // Position
  VECTOR v;      // Velocity
  float  m;      // Mass
  float  r;      // Radius
  U32    Index;  // Normally used as color
  int    Id;     // ID
  BALL * pNext;
};

typedef struct {
  int   CollisionType;
  int   WhichWall;
  float TimeToCollision;
} COLLISION;

typedef struct {
  int        xPos, yPos;
  int        xSize, ySize;
  void    (* pfDrawBk)  (WM_HWIN hWin, void * pConfig);
  void    (* pfDrawBall)(WM_HWIN hWin, void * pConfig, U32 Index, float x, float y, float r);
  unsigned   Range;
  unsigned * pRadius;
  unsigned   NumBalls;
  float      vMin, vMax;
  float      rMin, rMax;
  unsigned   TimeSlice;
  float      Gravity;
  int        HasBallGravity;
  int        HasGroundGravity;
  int        HasInitialVelocity;
  GUI_COLOR  ColorBk;  // Used if pfDrawBk() not set
} BALLSIM_CONFIG;

typedef struct {
  BALL           * pFirstBall;            // Stores all the balls
  int              HasWalls;              // Have wall boundaries been set?
  WALLS            Walls;
  int              NextId;                // Next ID to assign to an added ball
  unsigned         MaxCollisions;         // Max number of collisions per frame in advanceSim
  unsigned         MaxCollisionsPerBall;  // Max number of collisions per frame based on the number of balls
  float            MinArea;               // Minimum area within walls
  float            MaxDiameter;           // Maximum diameter out of all the balls
  unsigned         NumBalls;
  BALLSIM_CONFIG * pConfig;
} BALLSIM;

/*********************************************************************
*
*       Static (const) data
*
**********************************************************************
*/
static GUI_CONST_STORAGE GUI_COLOR _ColorsSeggerLogo_80x40[] = {
#if (GUI_USE_ARGB == 0)
  0x000000, 0xFFFFFF, 0x292929, 0x5F5F5F,
  0x96433D, 0xB0726E, 0xCACACA, 0xF8F3F3,
  0xD7D7D7, 0xEBDCDB, 0x363636, 0x9D4F49,
  0xAA6661, 0x949494, 0xA1A1A1, 0xB77E7A,
  0x444444, 0xBD8A86, 0xD1ADAA, 0xE4E4E4,
  0xF2F2F2, 0x6C6C6C, 0x797979, 0xA35B55,
  0xD8B9B6, 0xAFAFAF, 0xBCBCBC, 0xF2E8E7,
  0x515151, 0xCBA19E, 0xE5D0CF, 0x878787,
  0xC49592, 0xDEC4C2
#else
  0xFF000000, 0xFFFFFFFF, 0xFF292929, 0xFF5F5F5F,
  0xFF3D4396, 0xFF6E72B0, 0xFFCACACA, 0xFFF3F3F8,
  0xFFD7D7D7, 0xFFDBDCEB, 0xFF363636, 0xFF494F9D,
  0xFF6166AA, 0xFF949494, 0xFFA1A1A1, 0xFF7A7EB7,
  0xFF444444, 0xFF868ABD, 0xFFAAADD1, 0xFFE4E4E4,
  0xFFF2F2F2, 0xFF6C6C6C, 0xFF797979, 0xFF555BA3,
  0xFFB6B9D8, 0xFFAFAFAF, 0xFFBCBCBC, 0xFFE7E8F2,
  0xFF515151, 0xFF9EA1CB, 0xFFCFD0E5, 0xFF878787,
  0xFF9295C4, 0xFFC2C4DE
#endif

};

static GUI_CONST_STORAGE GUI_LOGPALETTE _PalSeggerLogo_80x40 = {
  34,  // Number of entries
  1,   // Has transparency
  &_ColorsSeggerLogo_80x40[0]
};

static GUI_CONST_STORAGE unsigned char _acSeggerLogo_80x40[] = {
  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
        0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
  0x02, 0x02, 0x0A, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0A, 0x02, 0x02,
  0x02, 0x02, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x02, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x21, 0x0F, 0x09, 0x01, 0x01, 0x01, 0x01, 0x09, 0x11, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x01, 0x05, 0x04, 0x0B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x20, 0x04, 0x04, 0x0C, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x0C, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x09, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x07, 0x0B, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x1E, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x12, 0x01, 0x01, 0x01, 0x09, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x12, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x1E, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x09, 0x0B, 0x04, 0x04, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x0B, 0x07, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x18, 0x04, 0x04, 0x0B, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x05, 0x01, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x11, 0x04, 0x04, 0x17, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1B, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x04, 0x1D, 0x01, 0x01, 0x01, 0x09, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x07, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1D, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04, 0x1E, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x1B, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1B, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04, 0x0B, 0x07, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x1D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04, 0x0B, 0x07, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x1D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x04, 0x04, 0x1E, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x1B, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1B, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x04, 0x1D, 0x01, 0x01, 0x01, 0x09, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x07, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 
        0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1D, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x05, 0x01, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x11, 0x04, 0x04, 0x17, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 
        0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1B, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x0B, 0x07, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x18, 0x04, 0x04, 0x0B, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x1E, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x09, 0x0B, 0x04, 0x04, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x12, 0x01, 0x01, 0x01, 0x09, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x12, 0x01, 0x01, 0x14, 0x1A, 0x0D, 0x0D, 0x1A, 0x01, 0x01, 0x01, 0x13, 0x06, 0x06, 0x06, 0x06, 0x06, 0x08, 0x01, 0x01, 0x01, 0x13, 0x19, 
        0x0D, 0x0E, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x13, 0x0E, 0x0D, 0x0E, 0x08, 0x01, 0x01, 0x01, 0x14, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x01, 0x01, 0x06, 0x06, 0x06, 0x06, 0x06, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x0F, 0x01, 0x01, 0x08, 0x0A, 0x02, 0x02, 0x02, 0x02, 0x10, 0x13, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x08, 0x01, 0x1F, 0x02, 0x02, 
        0x02, 0x02, 0x02, 0x03, 0x14, 0x01, 0x01, 0x1F, 0x02, 0x02, 0x02, 0x02, 0x02, 0x15, 0x01, 0x01, 0x16, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x06, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x1F, 0x02, 0x15, 0x13, 0x08, 0x1C, 0x02, 0x0D, 0x01, 0x03, 0x02, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x01, 0x19, 0x02, 0x02, 0x0D, 
        0x06, 0x19, 0x0A, 0x02, 0x19, 0x01, 0x0D, 0x02, 0x02, 0x0E, 0x06, 0x0E, 0x0A, 0x02, 0x06, 0x01, 0x03, 0x02, 0x16, 0x06, 0x06, 0x06, 0x06, 0x14, 0x06, 0x02, 0x1C, 0x06, 0x06, 0x0D, 0x02, 0x02, 0x06, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x12, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x1E, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x01, 0x1F, 0x02, 0x10, 0x0E, 0x08, 0x14, 0x06, 0x14, 0x01, 0x03, 0x02, 0x0E, 0x06, 0x06, 0x06, 0x13, 0x01, 0x03, 0x02, 0x16, 0x01, 
        0x01, 0x01, 0x08, 0x0D, 0x14, 0x01, 0x10, 0x02, 0x0D, 0x01, 0x01, 0x01, 0x08, 0x0E, 0x14, 0x01, 0x03, 0x02, 0x16, 0x06, 0x06, 0x06, 0x13, 0x01, 0x06, 0x02, 0x03, 0x01, 0x01, 0x14, 0x10, 0x02, 0x06, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x09, 0x04, 0x04, 0x0B, 0x09, 0x01, 0x01, 0x07, 0x0B, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x01, 0x08, 0x10, 0x02, 0x02, 0x02, 0x02, 0x15, 0x08, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0x02, 0x02, 0x1A, 0x01, 
        0x13, 0x03, 0x03, 0x03, 0x0E, 0x01, 0x02, 0x02, 0x06, 0x01, 0x08, 0x03, 0x03, 0x03, 0x0E, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0A, 0x01, 0x06, 0x02, 0x0A, 0x03, 0x03, 0x0A, 0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x07, 0x17, 0x04, 0x04, 0x18, 0x01, 0x01, 0x01, 0x0C, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x0D, 0x03, 0x02, 0x02, 0x1C, 0x01, 0x03, 0x02, 0x0E, 0x06, 0x06, 0x06, 0x13, 0x01, 0x10, 0x02, 0x0E, 0x01, 
        0x08, 0x0A, 0x02, 0x02, 0x03, 0x01, 0x02, 0x02, 0x1A, 0x01, 0x1A, 0x0A, 0x02, 0x02, 0x03, 0x01, 0x03, 0x02, 0x16, 0x06, 0x06, 0x06, 0x08, 0x01, 0x06, 0x02, 0x0A, 0x03, 0x03, 0x0A, 0x02, 0x1C, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x0F, 0x04, 0x04, 0x11, 0x01, 0x01, 0x01, 0x20, 0x04, 0x04, 0x0C, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0E, 0x02, 0x19, 0x01, 0x01, 0x1A, 0x02, 0x02, 0x01, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x16, 0x02, 0x10, 0x14, 
        0x01, 0x01, 0x0D, 0x02, 0x03, 0x01, 0x15, 0x02, 0x1C, 0x14, 0x01, 0x01, 0x16, 0x02, 0x03, 0x01, 0x03, 0x02, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x02, 0x03, 0x01, 0x01, 0x06, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x04, 0x04, 0x0C, 0x07, 0x01, 0x01, 0x01, 0x05, 0x04, 0x0B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0E, 0x02, 0x02, 0x03, 0x16, 0x10, 0x02, 0x15, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0A, 0x1A, 0x13, 0x0A, 0x02, 0x0A, 
        0x03, 0x1C, 0x02, 0x02, 0x03, 0x01, 0x08, 0x0A, 0x02, 0x0A, 0x03, 0x10, 0x02, 0x02, 0x03, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x19, 0x06, 0x02, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x08, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x21, 0x0F, 0x09, 0x01, 0x01, 0x01, 0x01, 0x09, 0x11, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x1C, 0x02, 0x02, 0x10, 0x16, 0x14, 0x01, 0x0D, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x01, 0x13, 0x16, 0x0A, 
        0x02, 0x0A, 0x1F, 0x15, 0x15, 0x01, 0x01, 0x08, 0x15, 0x0A, 0x02, 0x10, 0x0D, 0x03, 0x15, 0x01, 0x19, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x1A, 0x08, 0x03, 0x0D, 0x01, 0x01, 0x01, 0x15, 0x10, 0x13, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x02,
  0x02, 0x02, 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0x02, 0x02,
  0x02, 0x02, 0x0A, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0A, 0x02, 0x02,
  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
        0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02
};

static GUI_CONST_STORAGE GUI_BITMAP _bmSeggerLogo_80x40 = {
  80, // xSize
  40, // ySize
  80, // BytesPerLine
  8, // BitsPerPixel
  _acSeggerLogo_80x40,  // Pointer to picture data (indices)
  &_PalSeggerLogo_80x40   // Pointer to palette
};

static GUI_CONST_STORAGE unsigned char acGUI_Font32_AA4_Bounce_0042[152] = { /* code 0042, LATIN CAPITAL LETTER B */
  0x5E, 0xFF, 0xFF, 0xFF, 0xFE, 0xC7, 0x00, 0x00,
  0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x19, 0xFF, 0xFE, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFE, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x19, 0xFF, 0xFA, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD2, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x04, 0xDF, 0xFF, 0x70,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0x4F, 0xFF, 0xC0,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0x4F, 0xFF, 0xC0,
  0xFF, 0xFF, 0x00, 0x00, 0x04, 0xDF, 0xFF, 0x80,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x10,
  0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD3, 0x00,
  0x6E, 0xFF, 0xFF, 0xFF, 0xFE, 0xB6, 0x00, 0x00
};

static GUI_CONST_STORAGE unsigned char acGUI_Font32_AA4_Bounce_0063[ 84] = { /* code 0063, LATIN SMALL LETTER C */
  0x00, 0x06, 0xBE, 0xFE, 0xB6, 0x00,
  0x01, 0xCF, 0xFF, 0xFF, 0xFF, 0xC1,
  0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB,
  0x5F, 0xFF, 0xE4, 0x02, 0xBF, 0xFF,
  0xAF, 0xFF, 0x60, 0x00, 0x1B, 0xF8,
  0xEF, 0xFF, 0x20, 0x00, 0x00, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
  0xEF, 0xFF, 0x20, 0x00, 0x00, 0x00,
  0xBF, 0xFF, 0x70, 0x00, 0x1A, 0xF8,
  0x5F, 0xFF, 0xE4, 0x02, 0xBF, 0xFF,
  0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA,
  0x01, 0xCF, 0xFF, 0xFF, 0xFF, 0xB1,
  0x00, 0x07, 0xCF, 0xFE, 0xB5, 0x00
};

static GUI_CONST_STORAGE unsigned char acGUI_Font32_AA4_Bounce_0065[ 98] = { /* code 0065, LATIN SMALL LETTER E */
  0x00, 0x06, 0xCE, 0xFE, 0xB6, 0x00, 0x00,
  0x01, 0xCF, 0xFF, 0xFF, 0xFF, 0xC1, 0x00,
  0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00,
  0x5F, 0xFF, 0xD3, 0x03, 0xDF, 0xFF, 0x60,
  0xBF, 0xFF, 0x50, 0x00, 0x4F, 0xFF, 0xB0,
  0xEF, 0xFF, 0x10, 0x00, 0x1F, 0xFF, 0xE0,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0,
  0xDF, 0xFF, 0x10, 0x00, 0x00, 0x00, 0x00,
  0xAF, 0xFF, 0x50, 0x00, 0x00, 0x00, 0x00,
  0x5F, 0xFF, 0xE4, 0x00, 0x4B, 0xFB, 0x00,
  0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
  0x01, 0xCF, 0xFF, 0xFF, 0xFF, 0xE3, 0x00,
  0x00, 0x06, 0xCE, 0xFE, 0xB7, 0x10, 0x00
};

static GUI_CONST_STORAGE unsigned char acGUI_Font32_AA4_Bounce_006E[ 84] = { /* code 006E, LATIN SMALL LETTER N */
  0x6E, 0xE5, 0x06, 0xCF, 0xEB, 0x30,
  0xEF, 0xFC, 0x9F, 0xFF, 0xFF, 0xF3,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA,
  0xFF, 0xFF, 0xC2, 0x08, 0xFF, 0xFE,
  0xFF, 0xFF, 0x30, 0x02, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xEF, 0xFE, 0x00, 0x00, 0xEF, 0xFE,
  0x6E, 0xE6, 0x00, 0x00, 0x6E, 0xE6
};

static GUI_CONST_STORAGE unsigned char acGUI_Font32_AA4_Bounce_006F[ 98] = { /* code 006F, LATIN SMALL LETTER O */
  0x00, 0x04, 0xAD, 0xFF, 0xDA, 0x40, 0x00,
  0x01, 0xAF, 0xFF, 0xFF, 0xFF, 0xFA, 0x10,
  0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0,
  0x4F, 0xFF, 0xF6, 0x11, 0x6F, 0xFF, 0xF4,
  0xAF, 0xFF, 0x70, 0x00, 0x07, 0xFF, 0xFA,
  0xDF, 0xFF, 0x30, 0x00, 0x03, 0xFF, 0xFD,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF,
  0xDF, 0xFF, 0x30, 0x00, 0x03, 0xFF, 0xFD,
  0xAF, 0xFF, 0x70, 0x00, 0x07, 0xFF, 0xFA,
  0x4F, 0xFF, 0xF6, 0x11, 0x6F, 0xFF, 0xF4,
  0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0,
  0x01, 0xAF, 0xFF, 0xFF, 0xFF, 0xFA, 0x10,
  0x00, 0x04, 0xAD, 0xFF, 0xDA, 0x40, 0x00
};

static GUI_CONST_STORAGE unsigned char acGUI_Font32_AA4_Bounce_0075[ 84] = { /* code 0075, LATIN SMALL LETTER U */
  0x6E, 0xE6, 0x00, 0x00, 0x6E, 0xE6,
  0xEF, 0xFE, 0x00, 0x00, 0xEF, 0xFE,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
  0xFF, 0xFF, 0x10, 0x03, 0xFF, 0xFF,
  0xEF, 0xFF, 0x80, 0x2C, 0xFF, 0xFF,
  0xAF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x3F, 0xFF, 0xFF, 0xF9, 0xCF, 0xFE,
  0x04, 0xBF, 0xFC, 0x60, 0x5E, 0xE6
};

static GUI_CONST_STORAGE GUI_CHARINFO_EXT GUI_Font32_AA4_Bounce_CharInfo[6] = {
   {  15,  19,   2,   7,  18, acGUI_Font32_AA4_Bounce_0042 } /* code 0042, LATIN CAPITAL LETTER B */
  ,{  12,  14,   1,  12,  14, acGUI_Font32_AA4_Bounce_0063 } /* code 0063, LATIN SMALL LETTER C */
  ,{  13,  14,   1,  12,  15, acGUI_Font32_AA4_Bounce_0065 } /* code 0065, LATIN SMALL LETTER E */
  ,{  12,  14,   2,  12,  16, acGUI_Font32_AA4_Bounce_006E } /* code 006E, LATIN SMALL LETTER N */
  ,{  14,  14,   1,  12,  16, acGUI_Font32_AA4_Bounce_006F } /* code 006F, LATIN SMALL LETTER O */
  ,{  12,  14,   2,  12,  16, acGUI_Font32_AA4_Bounce_0075 } /* code 0075, LATIN SMALL LETTER U */
};

static GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Font32_AA4_Bounce_Prop5 = {
   0x0075 /* first character */
  ,0x0075 /* last character  */
  ,&GUI_Font32_AA4_Bounce_CharInfo[  5] /* address of first character */
  ,(GUI_CONST_STORAGE GUI_FONT_PROP_EXT *)0 /* pointer to next GUI_FONT_PROP_EXT */
};

static GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Font32_AA4_Bounce_Prop4 = {
   0x006E /* first character */
  ,0x006F /* last character  */
  ,&GUI_Font32_AA4_Bounce_CharInfo[  3] /* address of first character */
  ,&GUI_Font32_AA4_Bounce_Prop5 /* pointer to next GUI_FONT_PROP_EXT */
};

static GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Font32_AA4_Bounce_Prop3 = {
   0x0065 /* first character */
  ,0x0065 /* last character  */
  ,&GUI_Font32_AA4_Bounce_CharInfo[  2] /* address of first character */
  ,&GUI_Font32_AA4_Bounce_Prop4 /* pointer to next GUI_FONT_PROP_EXT */
};

static GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Font32_AA4_Bounce_Prop2 = {
   0x0063 /* first character */
  ,0x0063 /* last character  */
  ,&GUI_Font32_AA4_Bounce_CharInfo[  1] /* address of first character */
  ,&GUI_Font32_AA4_Bounce_Prop3 /* pointer to next GUI_FONT_PROP_EXT */
};

static GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Font32_AA4_Bounce_Prop1 = {
   0x0042 /* first character */
  ,0x0042 /* last character  */
  ,&GUI_Font32_AA4_Bounce_CharInfo[  0] /* address of first character */
  ,&GUI_Font32_AA4_Bounce_Prop2 /* pointer to next GUI_FONT_PROP_EXT */
};

static GUI_CONST_STORAGE GUI_FONT GUI_Font32_AA4_Bounce = {
   GUI_FONTTYPE_PROP_AA4_EXT /* type of font    */
  ,32 /* height of font  */
  ,32 /* space of font y */
  ,1 /* magnification x */
  ,1 /* magnification y */
  ,{&GUI_Font32_AA4_Bounce_Prop1}
  ,26 /* Baseline */
  ,14 /* Height of lowercase characters */
  ,19 /* Height of capital characters */
};

static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
  { WINDOW_CreateIndirect,   "Window",   ID_WINDOW_0,     0,   0, 280, 130, 0, 0x0,  0 },
  { BUTTON_CreateIndirect,   "Button",   ID_BUTTON_0,    10,  10, 120,  20, 0, 0x0,  0 },
  { BUTTON_CreateIndirect,   "Button",   ID_BUTTON_1,    10,  40, 120,  20, 0, 0x0,  0 },
  { BUTTON_CreateIndirect,   "Button",   ID_BUTTON_2,    10, 100, 260,  20, 0, 0x0,  0 },
  { CHECKBOX_CreateIndirect, "Checkbox", ID_CHECKBOX_0, 150,  40, 120,  20, 0, 0x0,  0 },
  { CHECKBOX_CreateIndirect, "Checkbox", ID_CHECKBOX_1, 150,  10, 120,  20, 0, 0x0,  0 },
  { TEXT_CreateIndirect,     "Text",     ID_TEXT_0,      10,  73, 120,  20, 0, 0x64, 0 },
  { SLIDER_CreateIndirect,   "Slider",   ID_SLIDER_0,   150,  70, 120,  20, 0, 0x0,  0 },
};

/*********************************************************************
*
*       Static code: Helper(s)
*
**********************************************************************
*/
/*********************************************************************
*
*       _Square
*/
static float _Square(float x) { 
  return x * x;
}

/*********************************************************************
*
*       _GetRandomNumber
*/
static float _GetRandomNumber(float min, float max) {
  return (max - min) * rand() / RAND_MAX + min;
}

#ifdef WIN32
static size_t _AllocatedBytes;
#endif

/*********************************************************************
*
*       _Free
*/
static void _Free(void * p) {
#ifdef WIN32
  _AllocatedBytes -= _msize(p);
#endif
  free(p);
}

/*********************************************************************
*
*       _Calloc
*/
static void * _Calloc(size_t Num, size_t Size) {
  void * p;
  p = calloc(Num, Size);
#ifdef WIN32
  _AllocatedBytes += _msize(p);
#endif
  return calloc(Num, Size);
}

/*********************************************************************
*
*       Static code: VECTOR
*
**********************************************************************
*/
/*********************************************************************
*
*       _VECTOR_Create
*/
static VECTOR * _VECTOR_Create(float x, float y) {
  VECTOR * pVectorNew;
  
  pVectorNew = (VECTOR *)_Calloc(sizeof(VECTOR), 1);
  pVectorNew->x = x;
  pVectorNew->y = y;
  return pVectorNew;
}

/*********************************************************************
*
*       _VECTOR_CreateCopy
*/
static VECTOR * _VECTOR_CreateCopy(VECTOR * pVector) {
  return _VECTOR_Create(pVector->x, pVector->y);
}

/*********************************************************************
*
*       _VECTOR_CreateCopyPlus
*/
static VECTOR * _VECTOR_CreateCopyPlus(VECTOR * pVector, VECTOR * pVector1) {
  return _VECTOR_Create(pVector->x + pVector1->x, pVector->y + pVector1->y);
}

/*********************************************************************
*
*       _VECTOR_CreateCopyMinus
*/
static VECTOR * _VECTOR_CreateCopyMinus(VECTOR * pVector, VECTOR * pVector1) {
  return _VECTOR_Create(pVector->x - pVector1->x, pVector->y - pVector1->y);
}

/*********************************************************************
*
*       _VECTOR_CreateCopyMult
*/
static VECTOR * _VECTOR_CreateCopyMult(VECTOR * pVector, float c) {
  return _VECTOR_Create(pVector->x * c, pVector->y * c);
}

/*********************************************************************
*
*       _VECTOR_Delete
*/
static void _VECTOR_Delete(VECTOR * pVector) {
  _Free(pVector);
}

/*********************************************************************
*
*       _VECTOR_SetX
*/
static void _VECTOR_SetX(VECTOR * pVector, float x) {
  pVector->x = x;
}

/*********************************************************************
*
*       _VECTOR_SetY
*/
static void _VECTOR_SetY(VECTOR * pVector, float y) {
  pVector->y = y;
}

/*********************************************************************
*
*       _VECTOR_SetXY
*/
static void _VECTOR_SetXY(VECTOR * pVector, float x, float y) {
  pVector->x = x;
  pVector->y = y;
}

/*********************************************************************
*
*       _VECTOR_Magnitude
*/
static float _VECTOR_Magnitude(VECTOR * pVector) { 
  return sqrt(pVector->x * pVector->x + pVector->y * pVector->y);
}

/*********************************************************************
*
*       _VECTOR_CreateUnitVector
*/
static VECTOR * _VECTOR_CreateUnitVector(VECTOR * pVector) {
  VECTOR * pVectorNew;
  float    Mag;

  Mag = _VECTOR_Magnitude(pVector);
  if (Mag != 0.f) {
    pVectorNew = _VECTOR_Create(pVector->x / Mag, pVector->y / Mag);
  } else {
    pVectorNew = _VECTOR_Create(0.f, 0.f);
  }
  return pVectorNew;
}

/*********************************************************************
*
*       _VECTOR_DotProduct
*/
static float _VECTOR_DotProduct(VECTOR * pVector, VECTOR * pVector1) {
  float DotProduct;

  DotProduct = pVector->x * pVector1->x + pVector->y * pVector1->y;
  return DotProduct;
}

/*********************************************************************
*
*       Static code: WALLS
*
**********************************************************************
*/
/*********************************************************************
*
*       _WALLS_Create
*/
static WALLS * _WALLS_Create(float x1, float y1, float x2, float y2) {
  WALLS * pWall;
  
  pWall = (WALLS *)_Calloc(sizeof(WALLS), 1);
  pWall->x1 = x1;
  pWall->y1 = y1;
  pWall->x2 = x2;
  pWall->y2 = y2;
  return pWall;
}

/*********************************************************************
*
*       _WALLS_CreateEmpty
*/
static WALLS * _WALLS_CreateEmpty(void) {
  return _WALLS_Create(0.f, 0.f, 0.f, 0.f);
}

/*********************************************************************
*
*       Static code: BALL
*
**********************************************************************
*/
/*********************************************************************
*
*       _BALL_Create
*/
static BALL * _BALL_Create(void) {
  BALL * pBall;
  
  pBall = (BALL *)_Calloc(sizeof(BALL), 1);
  pBall->m = 0.f;
  pBall->r = 0.f;
  return pBall;
}

/*********************************************************************
*
*       _BALL_AdvanceBallPosition
*/
static void _BALL_AdvanceBallPosition(BALL * pBall, const float dt) {
  _VECTOR_SetXY(&pBall->p, pBall->p.x + pBall->v.x * dt, pBall->p.y + pBall->v.y * dt);
}

/*********************************************************************
*
*        _BALL_DoBallGravity
*/
static void _BALL_DoBallGravity(BALL * pb0, BALL * pb1, const float dt, float g) {
  VECTOR * v_n;
  VECTOR * v_un;
  float    r, f, a0, a1, v0, v1;

  v_n  = _VECTOR_CreateCopyMinus(&pb0->p, &pb1->p);  // v_n = normal vec. - a vector normal to the collision surface
  v_un = _VECTOR_CreateUnitVector(v_n);              // unit normal vector
  r = _VECTOR_Magnitude(v_n);
  f = g * (pb0->m * pb1->m) / (r * r);
  a0 = f / pb0->m;
  a1 = f / pb1->m;
  v0 = a0 * dt;
  v1 = a1 * dt;
  _VECTOR_SetXY(&pb0->v, pb0->v.x - v_un->x * v0, pb0->v.y - v_un->y * v0);
  _VECTOR_SetXY(&pb1->v, pb1->v.x + v_un->x * v1, pb1->v.y + v_un->y * v1);
  _VECTOR_Delete(v_n);
  _VECTOR_Delete(v_un);
}

/*********************************************************************
*
*        _BALL_DoGroundGravity
*/
static void _BALL_DoGroundGravity(BALL * pb, const float dt, float g) {
  _VECTOR_SetY(&pb->v, pb->v.y + g * dt * 0.95);
}

/*********************************************************************
*
*       Static code: COLLISION
*
**********************************************************************
*/
/*********************************************************************
*
*       _COLLISION_Reset
*/
static void _COLLISION_Reset(COLLISION * pCollision) {
  pCollision->CollisionType   = TYPE_NONE;
  pCollision->WhichWall       = WALL_NONE;
  pCollision->TimeToCollision = 0.f;
}

/*********************************************************************
*
*       _COLLISION_Create
*/
static COLLISION * _COLLISION_Create(void) {
  COLLISION * pCollision;
  
  pCollision = (COLLISION *)_Calloc(sizeof(COLLISION), 1);
  _COLLISION_Reset(pCollision);
  return pCollision;
}

/*********************************************************************
*
*       _COLLISION_SetCollisionWithWall
*/
static void _COLLISION_SetCollisionWithWall(COLLISION * pCollision, const float t, int WhichWall) {
  pCollision->CollisionType   = TYPE_WALL;
  pCollision->WhichWall       = WhichWall;
  pCollision->TimeToCollision = t;
}

/*********************************************************************
*
*       _COLLISION_SetCollisionWithBall
*/
static void _COLLISION_SetCollisionWithBall(COLLISION * pCollision, const float t) {
  pCollision->CollisionType   = TYPE_BALL;
  pCollision->WhichWall       = WALL_NONE;
  pCollision->TimeToCollision = t;
}

/*********************************************************************
*
*       _COLLISION_Ball1HasCollision
*/
static int _COLLISION_Ball1HasCollision(COLLISION * pCollision) {
  return pCollision->CollisionType != TYPE_NONE;
}

/*********************************************************************
*
*       _COLLISION_Ball2HasCollision
*/
static int _COLLISION_Ball2HasCollision(COLLISION * pCollision) {
  return pCollision->CollisionType == TYPE_BALL;
}

/*********************************************************************
*
*       _COLLISION_Ball1HasCollisionWithWall
*/
static int _COLLISION_Ball1HasCollisionWithWall(COLLISION * pCollision) {
  return pCollision->CollisionType == TYPE_WALL;
}

/*********************************************************************
*
*       _COLLISION_Ball1HasCollisionWithBall
*/
static int _COLLISION_Ball1HasCollisionWithBall(COLLISION * pCollision) {
  return pCollision->CollisionType == TYPE_BALL;
}

/*********************************************************************
*
*       _COLLISION_GetCollisionWall
*/
static int _COLLISION_GetCollisionWall(COLLISION * pCollision) {
  return (int)pCollision->WhichWall;
}

/*********************************************************************
*
*        _COLLISION_FindTimeUntilTwoBallsCollide
*
* Function description:
*   Finds the time until two specified balls collide. If they don't collide,
*   the returned Collision will indicate that. If the balls are overlapping
*   a collision is NOT detected.
*/
static COLLISION _COLLISION_FindTimeUntilTwoBallsCollide(const BALL * pb1, const BALL * pb2) {
  COLLISION clsn = {0};
  float     a, b, c, det, t;
  
  // Compute parts of quadratic formula
  //
  // a = (v2x - v1x) ^ 2 + (v2y - v1y) ^ 2
  //
  a = _Square(pb2->v.x - pb1->v.x) + _Square(pb2->v.y - pb1->v.y);
  //
  // b = 2 * ((x20 - x10) * (v2x - v1x) + (y20 - y10) * (v2y - v1y))
  //
  b = 2.f * ((pb2->p.x - pb1->p.x) * (pb2->v.x - pb1->v.x) + (pb2->p.y - pb1->p.y) * (pb2->v.y - pb1->v.y));
  //
  // c = (x20 - x10) ^ 2 + (y20 - y10) ^ 2 - (r1 + r2) ^ 2
  //
  c = _Square(pb2->p.x - pb1->p.x) + _Square(pb2->p.y - pb1->p.y) - _Square(pb1->r + pb2->r);
  //
  // Determinant = b^2 - 4ac
  //
  det = _Square(b) - 4 * a * c;
  if (a != 0.f) {                     // If a == 0 then v2x==v1x and v2y==v1y and there will be no collision
    t = (-b - sqrt(det)) / (2. * a);  // Quadratic formula. t = time to collision
    if (t >= 0.) {                    // If collision occurs...
      _COLLISION_SetCollisionWithBall(&clsn, t);
    }
  }
  return clsn;
}

/*********************************************************************
*
*        _COLLISION_FindTimeUntilBallCollidesWithWall
*
* Function description:
*   Finds time until specified ball collides with any wall. If they
*   don't collide, the returned Collision indicates that. If there
*   will be collisions with more than one wall, this function returns
*   the earliest collision.
*
* IMPORTANT: This function assumes that the ball is bounded within 
*   the specified walls.
*/
static COLLISION _COLLISION_FindTimeUntilBallCollidesWithWall(const BALL * pb, const WALLS * pw) {
  COLLISION clsn = {0};
  float     timeToCollision;
  float     t;
  int       whichWall;
  
  timeToCollision = 0.f;
  whichWall       = WALL_NONE;
  //
  // Check for collision with wall X1
  //
  if (pb->v.x < 0.f) {
    t = (pb->r - pb->p.x + pw->x1) / pb->v.x;
    if (t >= 0.f) {  // If t < 0 then ball is headed away from wall
      timeToCollision = t;
      whichWall = WALL_X1;
    }
  }
  //
  // Check for collision with wall Y1
  //
  if (pb->v.y < 0.f) {
    t = (pb->r - pb->p.y + pw->y1) / pb->v.y;
    if (t >= 0.f) {
      if (whichWall == WALL_NONE || t < timeToCollision) {
        timeToCollision = t;
        whichWall = WALL_Y1;
      }
    }
  }
  //
  // Check for collision with wall X2
  //
  if (pb->v.x > 0.f) {
    t = (pw->x2 - pb->r - pb->p.x) / pb->v.x;
    if (t >= 0.f) {
      if (whichWall == WALL_NONE || t < timeToCollision) {
        timeToCollision = t;
        whichWall = WALL_X2;
      }
    }
  }
  //
  // Check for collision with wall Y2
  //
  if (pb->v.y > 0.f) {
    t = (pw->y2 - pb->r - pb->p.y) / pb->v.y;
    if (t >= 0.f) {
      if (whichWall == WALL_NONE || t < timeToCollision) {
        timeToCollision = t;
        whichWall = WALL_Y2;
      }
    }
  }
  //
  // Setup Collision return value
  //
  if (whichWall != WALL_NONE) {  // If there is a collision...
    _COLLISION_SetCollisionWithWall(&clsn, timeToCollision, whichWall);
  }
  return clsn;
}

/*********************************************************************
*
*        _COLLISION_DoElasticCollisionTwoBalls
*
* Function description:
*   Updates the velocities of b1 and b2 to reflect the effect of an elastic
*   collision between the two. IMPORTANT: This function does NOT check the
*   positions of the balls to see if they're actually colliding. It just
*   assumes that they are. Use findTimeUntilTwoBallsCollide() to see
*   if the balls are colliding.
*/
static void _COLLISION_DoElasticCollisionTwoBalls(BALL * pb1, BALL * pb2) {
  VECTOR * v_n;
  VECTOR * v_un;
  VECTOR * v_ut;
  VECTOR * v_v1nPrime;
  VECTOR * v_v1tPrime;
  VECTOR * v_v2nPrime;
  VECTOR * v_v2tPrime;
  float    v1n, v1t, v2n, v2t;
  float    v1tPrime, v2tPrime;
  float    v1nPrime, v2nPrime;

  //
  // Avoid division by zero below in computing new normal velocities
  // Doing a collision where both balls have no mass makes no sense anyway
  //
  if ((pb1->m == 0.f) && (pb2->m == 0.f)) {
    return;
  }
  //
  // Compute unit normal and unit tangent vectors
  //
  v_n  = _VECTOR_CreateCopyMinus(&pb2->p, &pb1->p);  // v_n = normal vec. - a vector normal to the collision surface
  v_un = _VECTOR_CreateUnitVector(v_n);              // unit normal vector
  v_ut = _VECTOR_Create(-v_un->y, v_un->x);          // unit tangent vector
  //
  // Compute scalar projections of velocities onto v_un and v_ut
  //
  v1n = _VECTOR_DotProduct(v_un, &pb1->v);  // Dot product
  v1t = _VECTOR_DotProduct(v_ut, &pb1->v);
  v2n = _VECTOR_DotProduct(v_un, &pb2->v);
  v2t = _VECTOR_DotProduct(v_ut, &pb2->v);
  //
  // Compute new tangential velocities
  //
  v1tPrime = v1t;  // Note: in reality, the tangential velocities do not change after the collision
  v2tPrime = v2t;
  //
  // Compute new normal velocities using one-dimensional elastic collision equations in the normal direction
  // Division by zero avoided. See early return above.
  //
  v1nPrime = (v1n * (pb1->m - pb2->m) + 2.f * pb2->m * v2n) / (pb1->m + pb2->m);
  v2nPrime = (v2n * (pb2->m - pb1->m) + 2.f * pb1->m * v1n) / (pb1->m + pb2->m);
  //
  // Compute new normal and tangential velocity vectors
  //
  v_v1nPrime = _VECTOR_CreateCopyMult(v_un, v1nPrime);  // Multiplication by a scalar
  v_v1tPrime = _VECTOR_CreateCopyMult(v_ut, v1tPrime);
  v_v2nPrime = _VECTOR_CreateCopyMult(v_un, v2nPrime);
  v_v2tPrime = _VECTOR_CreateCopyMult(v_ut, v2tPrime);
  //
  // Set new velocities in x and y coordinates
  //
  _VECTOR_SetXY(&pb1->v, v_v1nPrime->x + v_v1tPrime->x, v_v1nPrime->y + v_v1tPrime->y);
  _VECTOR_SetXY(&pb2->v, v_v2nPrime->x + v_v2tPrime->x, v_v2nPrime->y + v_v2tPrime->y);
  //
  // CleanUp
  //
  _VECTOR_Delete(v_n);
  _VECTOR_Delete(v_un);
  _VECTOR_Delete(v_ut);
  _VECTOR_Delete(v_v1nPrime);
  _VECTOR_Delete(v_v1tPrime);
  _VECTOR_Delete(v_v2nPrime);
  _VECTOR_Delete(v_v2tPrime);
}

/*********************************************************************
*
*        _COLLISION_DoElasticCollisionWithWall
*/
static void _COLLISION_DoElasticCollisionWithWall(BALL * pb, int w) {
  switch (w) {
  case WALL_X1:
    _VECTOR_SetX(&pb->v, fabs(pb->v.x));
    break;
  case WALL_Y1:
    _VECTOR_SetY(&pb->v, fabs(pb->v.y));
    break;
  case WALL_X2:
    _VECTOR_SetX(&pb->v, -fabs(pb->v.x));
    break;
  case WALL_Y2:
    _VECTOR_SetY(&pb->v, -fabs(pb->v.y));
    break;
  }
}

/*********************************************************************
*
*       Static code: BALLSIM
*
**********************************************************************
*/
/*********************************************************************
*
*        _BALLSIM_ResetBalls
*/
static void _BALLSIM_ResetBalls(BALLSIM * pBallsim) {
  BALL * pBalli;
  BALL * pBalld;
  
  pBallsim->NextId        = 0;   // Reset ID counter
  pBallsim->MinArea       = 0.;
  pBallsim->MaxDiameter   = 0.;
  pBallsim->MaxCollisions = 10;  // This will be overwritten on the first call to addBall()
  //
  // Delete all balls
  //
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    pBalld = pBalli;
    pBalli = pBalli->pNext;
    _Free(pBalld);
  }
}

/*********************************************************************
*
*        _BALLSIM_Create
*/
static BALLSIM * _BALLSIM_Create(void) {
  BALLSIM * pBallsim;

  pBallsim = (BALLSIM *)_Calloc(sizeof(BALLSIM), 1);
  pBallsim->HasWalls = 0;
  pBallsim->MaxCollisionsPerBall = 10;
  _BALLSIM_ResetBalls(pBallsim);
  return pBallsim;
}

/*********************************************************************
*
*        _BALLSIM_AdvanceBallPositions
*/
static void _BALLSIM_AdvanceBallPositions(BALLSIM * pBallsim, const float dt) {
  BALL * pBalli;

  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    _BALL_AdvanceBallPosition(pBalli, dt);
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*        BALLSSIM_FindEarliestCollisionOfTwoBalls
*/
static COLLISION BALLSSIM_FindEarliestCollisionOfTwoBalls(BALLSIM * pBallsim, BALL ** ppb1, BALL ** ppb2) {
  COLLISION earliestCollision;
  COLLISION c;
  BALL    * pBalli;
  BALL    * pBallj;
  
  _COLLISION_Reset(&earliestCollision);
  //
  // Compare each pair of balls. Index i runs from the first
  // ball up through the second-to-last ball. For each value of
  // i, index j runs from the ball after i up through the last ball.
  //
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    pBallj = pBallsim->pFirstBall;
    while (pBallj) {
      c = _COLLISION_FindTimeUntilTwoBallsCollide(pBalli, pBallj);
      if (_COLLISION_Ball1HasCollisionWithBall(&c)) {
        if (!_COLLISION_Ball1HasCollision(&earliestCollision) || c.TimeToCollision < earliestCollision.TimeToCollision) {
          earliestCollision = c;
          *ppb1 = pBalli;
          *ppb2 = pBallj;
        }
      }
      pBallj = pBallj->pNext;
    }
    pBalli = pBalli->pNext;
  }
  return earliestCollision;
}

/*********************************************************************
*
*        BALLSSIM_FindEarliestCollisionWithWall
*/
static COLLISION BALLSSIM_FindEarliestCollisionWithWall(BALLSIM * pBallsim, BALL ** ppb) {
  COLLISION earliestCollision;
  COLLISION c;
  BALL    * pBalli;
  
  _COLLISION_Reset(&earliestCollision);
  //
  // If there are no walls, return no collision
  //
  if (!pBallsim->HasWalls) {
    return earliestCollision;
  }
  //
  // Check each ball to see if any collide. Store the earliest colliding ball.
  //
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    c = _COLLISION_FindTimeUntilBallCollidesWithWall(pBalli, &pBallsim->Walls);
    if (_COLLISION_Ball1HasCollisionWithWall(&c)) {
      if (!_COLLISION_Ball1HasCollision(&earliestCollision) || c.TimeToCollision < earliestCollision.TimeToCollision) {
        earliestCollision = c;
        *ppb = pBalli;
      }
    }
    pBalli = pBalli->pNext;
  }
  return earliestCollision;
}

/*********************************************************************
*
*        BALLSSIM_FindEarliestCollision
*/
static COLLISION BALLSSIM_FindEarliestCollision(BALLSIM * pBallsim, BALL ** ppb1, BALL ** ppb2) {
  COLLISION earliestCollision;
  COLLISION cWalls;
  BALL    * pbCollideWithWall;
  
  earliestCollision = BALLSSIM_FindEarliestCollisionOfTwoBalls(pBallsim, ppb1, ppb2);
  if (pBallsim->HasWalls) {
    cWalls = BALLSSIM_FindEarliestCollisionWithWall(pBallsim, &pbCollideWithWall);
    if (_COLLISION_Ball1HasCollisionWithWall(&cWalls)) {
      if (!_COLLISION_Ball1HasCollisionWithBall(&earliestCollision) || cWalls.TimeToCollision < earliestCollision.TimeToCollision) {
        earliestCollision = cWalls;
        *ppb1 = pbCollideWithWall;
      }
    }
  }
  return earliestCollision;
}

/*********************************************************************
*
*        _BALLSIM_AdvanceBallGravity
*/
static void _BALLSIM_AdvanceBallGravity(BALLSIM * pBallsim, const float dt) {
  BALL    * pBalli;
  BALL    * pBallj;
  
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    pBallj = pBalli->pNext;
    while (pBallj) {
      _BALL_DoBallGravity(pBalli, pBallj, dt, pBallsim->pConfig->Gravity);
      pBallj = pBallj->pNext;
    }
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*       _BALLSIM_AdvanceGroundGravity
*/
static void _BALLSIM_AdvanceGroundGravity(BALLSIM * pBallsim, const float dt) {
  BALL * pBalli;

  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    _BALL_DoGroundGravity(pBalli, dt, pBallsim->pConfig->Gravity);
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*        BALLSSIM_AdvanceSim
*/
static void BALLSSIM_AdvanceSim(BALLSIM * pBallsim, const float dt) {
  float     tElapsed;
  COLLISION c;
  BALL    * pb1;
  BALL    * pb2;
  unsigned  i;

  tElapsed = 0.f;
  for (i = 0; i < pBallsim->MaxCollisions; i++) {
    //
    // Find earliest collision
    //
    c = BALLSSIM_FindEarliestCollision(pBallsim, &pb1, &pb2);
    //
    // If no collisions, break
    //
    if (!_COLLISION_Ball1HasCollision(&c)) {
      break;
    }
    //
    // Is collision within the time frame?
    // Note: condition is tElapsed + timeToCollision strictly < dt, not <=, because if the two were exactly
    // equal, we would perform the velocity adjustment for collision but not move the balls any more, so the
    // collision could be detected again on the next call to advanceSim().
    //
    if (tElapsed + c.TimeToCollision < dt) {
      //
      // Collision is within time frame
      // Advance balls to point of collision
      //
      _BALLSIM_AdvanceBallPositions(pBallsim, c.TimeToCollision);
      //
      // Collision is now occuring. Do collision calculation
      //
      if (_COLLISION_Ball1HasCollisionWithWall(&c)) {
        _COLLISION_DoElasticCollisionWithWall(pb1, c.WhichWall);
      } else if (_COLLISION_Ball1HasCollisionWithBall(&c)) {
        _COLLISION_DoElasticCollisionTwoBalls(pb1, pb2);
      }
      tElapsed += c.TimeToCollision;  // Move time counter forward
    } else {
      break;  // Break if collision is not within this frame
    }
  }
  //
  // Advance ball positions further if necessary after any collisions to complete the time frame
  //
  _BALLSIM_AdvanceBallPositions(pBallsim, dt - tElapsed);
  //
  // Manage ball gravity
  //
  if (pBallsim->pConfig->HasBallGravity) {
    _BALLSIM_AdvanceBallGravity(pBallsim, dt);
  }
  //
  // Manage ground gravity
  //
  if (pBallsim->pConfig->HasGroundGravity) {
    _BALLSIM_AdvanceGroundGravity(pBallsim, dt);
  }
}

/*********************************************************************
*
*        BALLSSIM_MoveBallToWithinBounds
*/
static void BALLSSIM_MoveBallToWithinBounds(BALLSIM * pBallsim, BALL * pb) {
  //
  // Check wall X1
  //
  if (pb->p.x - pb->r < pBallsim->Walls.x1) {
    _VECTOR_SetX(&pb->p, pBallsim->Walls.x1 + pb->r);
  }
  //
  // Check wall Y1
  //
  if (pb->p.y - pb->r < pBallsim->Walls.y1) {
    _VECTOR_SetY(&pb->p, pBallsim->Walls.y1 + pb->r);
  }
  //
  // Check wall X2
  //
  if (pb->p.x + pb->r > pBallsim->Walls.x2) {
    _VECTOR_SetX(&pb->p, pBallsim->Walls.x2 - pb->r);
  }
  //
  // Check wall Y2
  //
  if (pb->p.y + pb->r > pBallsim->Walls.y2) {
    _VECTOR_SetY(&pb->p, pBallsim->Walls.y2 - pb->r);
  }
}

/*********************************************************************
*
*        BALLSSIM_MoveWalls
*/
static void BALLSSIM_MoveWalls(BALLSIM * pBallsim, const WALLS * pNewWalls) {
  BALL * pBalli;

  pBallsim->Walls = *pNewWalls;
  pBallsim->HasWalls = 1;
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    BALLSSIM_MoveBallToWithinBounds(pBallsim, pBalli);
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*        BALLSSIM_GetMinWallDimension
*/
static float BALLSSIM_GetMinWallDimension(BALLSIM * pBallsim, float fixedWallDimension) {
  float minDimension;
  
  minDimension = 0.f;
  if (fixedWallDimension > 0.f) {
    minDimension = 4.f * pBallsim->MinArea / fixedWallDimension;
  }
  if (minDimension <= pBallsim->MaxDiameter) {
    minDimension = pBallsim->MaxDiameter + 1;
  }
  return minDimension;
}

/*********************************************************************
*
*        BALLSSIM_AddBall
*/
static void BALLSSIM_AddBall(BALLSIM * pBallsim, BALL * pNewBall) {
  BALL * pBalli;

  pBallsim->NumBalls = 0;
  pBalli = pBallsim->pFirstBall;
  if (pBalli) {
    pBallsim->NumBalls++;
    while (pBalli->pNext) {
      pBallsim->NumBalls++;
      pBalli = pBalli->pNext;
    }
    pBallsim->NumBalls++;
    pBalli->pNext = pNewBall;
  } else {
    pBallsim->NumBalls++;
    pBallsim->pFirstBall = pNewBall;
    pNewBall->pNext = NULL;
  }
  pBallsim->MaxCollisions = pBallsim->MaxCollisionsPerBall * pBallsim->NumBalls;
  BALLSSIM_MoveBallToWithinBounds(pBallsim, pNewBall);
  if (pNewBall->r * 2.f > pBallsim->MaxDiameter) {
    pBallsim->MaxDiameter = pNewBall->r * 2.f;
    pBallsim->MinArea += 4.f * pNewBall->r * pNewBall->r;
  }
}

/*********************************************************************
*
*       _BALLSIM_GetBallFromPos
*/
static BALL * _BALLSIM_GetBallFromPos(BALLSIM * pBallsim, float xPos, float yPos) {
  float d, dx, dy;
  BALL * pBalli;
  
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    dx = xPos - pBalli->p.x;
    dy = yPos - pBalli->p.y;
    d = sqrt(dx * dx + dy * dy);
    if (d < pBalli->r) {
      return pBalli;
    }
    pBalli = pBalli->pNext;
  }
  return NULL;
}

/*********************************************************************
*
*       _BALLSIM_DeleteBall
*/
static void _BALLSIM_DeleteBall(BALLSIM * pBallsim, BALL * pBall) {
  BALL * pBalli;
  BALL * pBallPrev = NULL;

  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    if (pBalli == pBall) {
      if (pBallPrev) {
        pBallPrev->pNext = pBalli->pNext;
      } else {
        pBallsim->pFirstBall = pBallsim->pFirstBall->pNext;
      }
      pBallsim->NumBalls--;
      _Free(pBall);
      break;
    }
    pBallPrev = pBalli;
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*       _BALLSIM_StopMoving
*/
static void _BALLSIM_StopMoving(BALLSIM * pBallsim) {
  BALL * pBalli;

  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    _VECTOR_SetXY(&pBalli->v, 0.f, 0.f);
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*       _BALLSIM_Delete
*/
static void _BALLSIM_Delete(BALLSIM * pBallsim) {
  _BALLSIM_ResetBalls(pBallsim);
  _Free(pBallsim);
}

/*********************************************************************
*
*       Static code: Ballsim Window
*
**********************************************************************
*/
/*********************************************************************
*
*       _UpdateDisplay
*/
static void _UpdateDisplay(WM_HWIN hWin, BALLSIM * pBallsim) {
  BALL * pBalli;
  U32    Color;

  //
  // Draw background
  //
  if (pBallsim->pConfig->pfDrawBk) {
    pBallsim->pConfig->pfDrawBk(hWin, pBallsim->pConfig);
  } else {
    GUI_SetBkColor(pBallsim->pConfig->ColorBk);
    GUI_Clear();
  }
  //
  // Draw all balls
  //
  pBalli = pBallsim->pFirstBall;
  while (pBalli) {
    if (pBallsim->pConfig->pfDrawBall) {
      pBallsim->pConfig->pfDrawBall(hWin, pBallsim->pConfig, pBalli->Index, pBalli->p.x, pBalli->p.y, pBalli->r);
    } else {
      Color = GUI_MAKE_COLOR(pBalli->Index);
      GUI_SetColor(Color);
      GUI_FillCircle(pBalli->p.x, pBalli->p.y, pBalli->r);
    }
    pBalli = pBalli->pNext;
  }
}

/*********************************************************************
*
*       _AddRandomBalls
*/
static void _AddRandomBalls(BALLSIM * pBallsim) {
  unsigned i, PosOccupied, Cnt;
  float d, dx, dy;
  BALL * pBall;
  BALL * pBalli;
  BALLSIM_CONFIG * pConfig;

  pConfig = pBallsim->pConfig;
  for (i = 0; i < pConfig->NumBalls; i++) {
    pBall = _BALL_Create();
    if (pBallsim->pConfig->HasInitialVelocity) {
      pBall->v.x = _GetRandomNumber(pConfig->vMin, pConfig->vMax);
      pBall->v.y = _GetRandomNumber(pConfig->vMin, pConfig->vMax);
    }
    pBall->Index = (U32)_GetRandomNumber(0, pConfig->Range);
    if (pConfig->pRadius) {
      pBall->r = *(pConfig->pRadius + pBall->Index);
    } else {
      pBall->r = _GetRandomNumber(pConfig->rMin, pConfig->rMax);
    }
    pBall->m = M_TO_A_RATIO * M_PI * pBall->r * pBall->r;
    //
    // Generate legal position
    //
    Cnt = 0;
    do {
      pBall->p.x = _GetRandomNumber((float)pBall->r, (float)(pConfig->xSize) - pBall->r);
      pBall->p.y = _GetRandomNumber((float)pBall->r, (float)(pConfig->ySize) - pBall->r);
      PosOccupied = 0;
      pBalli = pBallsim->pFirstBall;
      while (pBalli) {
        dx = pBalli->p.x - pBall->p.x;
        dy = pBalli->p.y - pBall->p.y;
        d = sqrt(dx * dx + dy * dy);
        if (d < pBalli->r + pBall->r) {
          PosOccupied = 1;
          break;
        }
        pBalli = pBalli->pNext;
      }
    } while (PosOccupied && (Cnt++ < 10));
    //
    // Add ball to array
    //
    if (PosOccupied == 0) {
      BALLSSIM_AddBall(pBallsim, pBall);
    }
  }
}

/*********************************************************************
*
*       _cbConfig
*/
static void _cbConfig(WM_MESSAGE * pMsg) {
  WM_HWIN                     hItem;
  static BALLSIM            * pBallsim;
  int                         Id, NCode;
  int                         xSize, ySize;
  WM_PID_STATE_CHANGED_INFO * pInfo;
  GUI_PID_STATE             * pState;
  static int                  IsDown;
  static GUI_POINT            pDown;

  switch (pMsg->MsgId) {
  case WM_PAINT:
    xSize = WM_GetWindowSizeX(pMsg->hWin);
    ySize = WM_GetWindowSizeY(pMsg->hWin);
    GUI_SetColor(GUI_BLACK);
    GUI_DrawRect(0, 0, xSize - 1, ySize - 1);
    break;
  case WM_PID_STATE_CHANGED:
    pInfo = (WM_PID_STATE_CHANGED_INFO *)pMsg->Data.p;
    if (pInfo->StatePrev == 0) {
      IsDown = 1;
      pDown.x = pInfo->x;
      pDown.y = pInfo->y;
      WM_SetCapture(pMsg->hWin, 1);
    } else {
      IsDown = 0;
    }
    break;
  case WM_TOUCH:
    if (IsDown) {
      pState = (GUI_PID_STATE *)pMsg->Data.p;
      if (pState) {
        WM_MoveWindow(pMsg->hWin, pState->x - pDown.x, pState->y - pDown.y);
      }
    }
    break;
  case WM_USER_DATA:
    WM_GetUserData(pMsg->hWin, &pBallsim, sizeof(void *));
    WINDOW_SetBkColor(pMsg->hWin, GUI_WHITE);
    WM_MakeModal(pMsg->hWin);
    hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0);
    BUTTON_SetText(hItem, "Stop moving");
    hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_1);
    BUTTON_SetText(hItem, "Create random balls");
    hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_2);
    BUTTON_SetText(hItem, "Back");
    hItem = WM_GetDialogItem(pMsg->hWin, ID_CHECKBOX_0);
    CHECKBOX_SetText(hItem, "Ball Gravity");
    if (pBallsim->pConfig->HasBallGravity) {
      CHECKBOX_Check(hItem);
    } else {
      CHECKBOX_Uncheck(hItem);
    }
    hItem = WM_GetDialogItem(pMsg->hWin, ID_CHECKBOX_1);
    CHECKBOX_SetText(hItem, "Ground Gravity");
    if (pBallsim->pConfig->HasGroundGravity) {
      CHECKBOX_Check(hItem);
    } else {
      CHECKBOX_Uncheck(hItem);
    }
    hItem = WM_GetDialogItem(pMsg->hWin, ID_TEXT_0);
    TEXT_SetText(hItem, "Gravity:");
    TEXT_SetTextAlign(hItem, GUI_TA_RIGHT | GUI_TA_VCENTER);
    hItem = WM_GetDialogItem(pMsg->hWin, ID_SLIDER_0);
    SLIDER_SetRange(hItem, 0, 400);
    SLIDER_SetValue(hItem, (int)pBallsim->pConfig->Gravity);
    break;
  case WM_NOTIFY_PARENT:
    Id    = WM_GetId(pMsg->hWinSrc);
    NCode = pMsg->Data.v;
    switch(Id) {
    case ID_BUTTON_0:
      switch(NCode) {
      case WM_NOTIFICATION_RELEASED:
        _BALLSIM_StopMoving(pBallsim);
        break;
      }
      break;
    case ID_BUTTON_1:
      switch(NCode) {
      case WM_NOTIFICATION_RELEASED:
        _AddRandomBalls(pBallsim);
        break;
      }
      break;
    case ID_BUTTON_2:
      switch(NCode) {
      case WM_NOTIFICATION_RELEASED:
        WM_DeleteWindow(pMsg->hWin);
        break;
      }
      break;
    case ID_CHECKBOX_0:
      switch(NCode) {
      case WM_NOTIFICATION_VALUE_CHANGED:
        pBallsim->pConfig->HasBallGravity = CHECKBOX_IsChecked(pMsg->hWinSrc) ? 1 : 0;
        break;
      }
      break;
    case ID_CHECKBOX_1:
      switch(NCode) {
      case WM_NOTIFICATION_VALUE_CHANGED:
        pBallsim->pConfig->HasGroundGravity = CHECKBOX_IsChecked(pMsg->hWinSrc) ? 1 : 0;
        break;
      }
      break;
    case ID_SLIDER_0:
      switch(NCode) {
      case WM_NOTIFICATION_VALUE_CHANGED:
        pBallsim->pConfig->Gravity = (float)SLIDER_GetValue(pMsg->hWinSrc);
        break;
      }
      break;
    }
    break;
  default:
    WM_DefaultProc(pMsg);
  }
}

/*********************************************************************
*
*       _CreateConfigWindow
*/
static void _CreateConfigWindow(BALLSIM * pBallsim, WM_HWIN hParent) {
  WM_HWIN hWin;
  int xPos, yPos;

  xPos = (pBallsim->pConfig->xSize - _aDialogCreate[0].xSize) / 2;
  yPos = (pBallsim->pConfig->ySize - _aDialogCreate[0].ySize) / 3;
  hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbConfig, WM_HBKWIN, xPos, yPos);
  WM_SetUserData(hWin, &pBallsim, sizeof(void *));
}

/*********************************************************************
*
*       _CreateBallsim
*/
static BALLSIM * _CreateBallsim(WM_HWIN hWin) {
  BALLSIM_CONFIG * pConfig;
  BALLSIM        * pBallsim;
  WALLS          * pWalls;

  WM_GetUserData(hWin, &pConfig, sizeof(void *));
  pBallsim = _BALLSIM_Create();
  pBallsim->pConfig = pConfig;
  pWalls = _WALLS_Create(0.f, 0.f, (float)pConfig->xSize, (float)pConfig->ySize);
  BALLSSIM_MoveWalls(pBallsim, pWalls);
  _AddRandomBalls(pBallsim);
  _Free(pWalls);
  return pBallsim;
}

/*********************************************************************
*
*       _cbBallsim
*/
static void _cbBallsim(WM_MESSAGE * pMsg) {
  static BALLSIM            * pBallsim;
  static GUI_TIMER_TIME       Time;
  GUI_TIMER_TIME              tNow;
  WM_HTIMER                   hTimer;
  WM_PID_STATE_CHANGED_INFO * pInfo;
  BALL                      * pBall;
  
  switch (pMsg->MsgId) {
  case WM_PID_STATE_CHANGED:
    pInfo = (WM_PID_STATE_CHANGED_INFO *)pMsg->Data.p;
    if (pInfo->StatePrev == 0) {
      pBall = _BALLSIM_GetBallFromPos(pBallsim, (float)pInfo->x, (float)pInfo->y);
      if (pBall) {
        _BALLSIM_DeleteBall(pBallsim, pBall);
      } else {
        _CreateConfigWindow(pBallsim, pMsg->hWin);
      }
    }
    break;
  case WM_DELETE:
    _BALLSIM_Delete(pBallsim);
    break;
  case WM_USER_DATA:
    pBallsim = _CreateBallsim(pMsg->hWin);
    hTimer   = WM_CreateTimer(pMsg->hWin, 0, TIME_SLICE, 0);
    Time     = GUI_GetTime();
    break;
  case WM_PAINT:
    _UpdateDisplay(pMsg->hWin, pBallsim);
    break;
  case WM_TIMER:
    WM_RestartTimer(pMsg->Data.v, pBallsim->pConfig->TimeSlice);
    tNow = GUI_GetTime();
    BALLSSIM_AdvanceSim(pBallsim, (float)(tNow - Time) / 1000);
    Time = tNow;
    WM_InvalidateWindow(pMsg->hWin);
    break;
  default:
    WM_DefaultProc(pMsg);
  }
}

/*********************************************************************
*
*       _CreateBallsimWindow
*/
static WM_HWIN _CreateBallsimWindow(BALLSIM_CONFIG * pConfig, WM_HWIN hParent) {
  WM_HWIN hWin;
  
  hWin = WM_CreateWindowAsChild(pConfig->xPos, pConfig->yPos, pConfig->xSize, pConfig->ySize, hParent, WM_CF_SHOW, _cbBallsim, sizeof(void *));
  WM_SetUserData(hWin, &pConfig, sizeof(void *));
#if GUI_VERSION < 54400
  WM_SendMessageNoPara(hWin, WM_USER_DATA);
#endif
  return hWin;
}
/*********************************************************************
*
*       Static code: Application
*
**********************************************************************
*/
/*********************************************************************
*
*       _DrawBk
*/
static void _DrawBk(WM_HWIN hWin, void * pVoid) {
  GUI_SetBkColor(GUI_MAKE_COLOR(0x202020));
  GUI_Clear();
}

/*********************************************************************
*
*       _DrawBall
*/
static void _DrawBall(WM_HWIN hWin, void * pVoid, U32 Index, float x, float y, float r) {
  GUI_SetColor(GUI_MAKE_COLOR(Index));
  GUI_AA_FillCircle((int)x, (int)y, (int)r); 
}

/*********************************************************************
*
*       _cbSplash
*/
static void _cbSplash(WM_MESSAGE * pMsg) {
  int xSize, ySize;
  const GUI_BITMAP * pBm;

  switch (pMsg->MsgId) {
  case WM_PAINT:
    xSize = WM_GetWindowSizeX(pMsg->hWin);
    ySize = WM_GetWindowSizeY(pMsg->hWin);
    GUI_SetBkColor(GUI_WHITE);
    GUI_Clear();
    pBm = &_bmSeggerLogo_80x40;
    GUI_DrawBitmap(pBm, xSize - pBm->XSize - 10, 5);
    GUI_SetColor(GUI_BLACK);
    GUI_SetFont(&GUI_Font32_AA4_Bounce);
    GUI_DispStringAt("Bounce", 10, 10);
    GUI_SetFont(GUI_FONT_13_ASCII);
    GUI_DispStringHCenterAt("Press screen for config dialog,\n"
                            "Tap balls to remove them.\n\n"
                            "Have fun...", xSize / 2, pBm->YSize + 20);
    break;
  case WM_CREATE:
    WM_CreateTimer(pMsg->hWin, 0, PERIOD_SPLASH, 0);
    WM_MakeModal(pMsg->hWin);
    break;
  case WM_TIMER:
    WM_DeleteWindow(pMsg->hWin);
    break;
  default:
    WM_DefaultProc(pMsg);
  }
}

/*********************************************************************
*
*       _Test
*/
static void _Test(void) {
  int xSize, ySize;
  int xPos, yPos;
  WM_HWIN hWin;
  static BALLSIM_CONFIG Config;

  xSize             = LCD_GetXSize();
  ySize             = LCD_GetYSize();
  //
  // Configuration of ball simulation
  //
  Config.xSize      = xSize;
  Config.ySize      = ySize;
  Config.Range      = 0xE0E0E0;
  Config.NumBalls   = NUM_BALLS;
  Config.TimeSlice  = TIME_SLICE;
  Config.vMin       = MIN_RANDOM_V;
  Config.vMax       = MAX_RANDOM_V;
  Config.rMin       = MIN_RANDOM_R;
  Config.rMax       = MAX_RANDOM_R;
  Config.Gravity    = GRAVITY;
  Config.pfDrawBk   = _DrawBk;
  Config.pfDrawBall = _DrawBall;
  hWin = _CreateBallsimWindow(&Config, WM_HBKWIN);
  //
  // Create splash screen
  //
  xPos = (xSize - XSIZE_SPLASH) / 2;
  yPos = (ySize - YSIZE_SPLASH) / 3;
  WM_CreateWindowAsChild(xPos, yPos, XSIZE_SPLASH, YSIZE_SPLASH, hWin, WM_CF_SHOW, _cbSplash, 0);
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/
/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {
  GUI_Init();
  WM_MULTIBUF_Enable(1);
  BUTTON_SetReactOnLevel();
  _Test();
  while (1) {
    GUI_Delay(100);
  }
}

/*************************** End of file ****************************/
